Desafio do Módulo 1 do Bootcamp IGTI Arquiteto de Big Data.
Enunciado: Uma operadora de seguro de saúde identificou, na sua base de dados de clientes, a relação entre os dados de colesterol e peso com a incidência de problemas que influenciam no desenvolvimento de doenças cardíacas. Pensando no bem-estar dos seus clientes, e ao mesmo tempo pensando em diminuir problemas de internação e tratamento para esses tipos de causa, a operadora quer realizar um estudo para identificar o perfil de pessoas que se encaixam nos grupos de risco e assim realizar medidas preventivas e palestras de cuidados médicos. Para isso, a operadora conta com a equipe de arquiteto de big data para ajudá-los a encontrar o grupo de risco dentre essas pessoas.
Através da análise dos dados, os analistas da operadora de seguro de saúde identificaram 4 grandes grupos:
Atividades:
Os alunos deverão desempenhar as seguintes atividades:
Obs.: para o cálculo do WCSS, vocês devem escolher apenas os atributos de peso e colesterol.
import pandas as pd
dados_clientes = pd.read_excel('dados_clientes-1.xlsx')
dados_clientes.head()
| id_cliente | peso | colesterol | genero | id_estado | |
|---|---|---|---|---|---|
| 0 | 1 | 102.0 | 111 | Masculino | 23 |
| 1 | 2 | 115.0 | 135 | Masculino | 7 |
| 2 | 3 | 115.0 | 136 | Masculino | 4 |
| 3 | 4 | 140.0 | 167 | Feminino | 24 |
| 4 | 5 | 130.0 | 158 | Masculino | 26 |
estados = pd.read_csv('estados_brasileiros-1.csv', encoding='latin-1', sep=';')
estados.head()
| id_estado | estado | sigla_estado | pais | |
|---|---|---|---|---|
| 0 | 1 | Acre | ac | Brasil |
| 1 | 2 | Alagoas | al | Brasil |
| 2 | 3 | Amapá | ap | Brasil |
| 3 | 4 | Amazonas | am | Brasil |
| 4 | 5 | Bahia | ba | Brasil |
idade_clientes = pd.read_csv('idade_clientes-1.csv', sep = ';')
idade_clientes.head()
| id_cliente | idade | |
|---|---|---|
| 0 | 1 | 17 |
| 1 | 2 | 28 |
| 2 | 3 | 62 |
| 3 | 4 | 55 |
| 4 | 5 | 44 |
dados_clientes.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 547 entries, 0 to 546 Data columns (total 5 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 id_cliente 547 non-null int64 1 peso 542 non-null float64 2 colesterol 547 non-null int64 3 genero 547 non-null object 4 id_estado 547 non-null int64 dtypes: float64(1), int64(3), object(1) memory usage: 21.5+ KB
dados_clientes.describe()
| id_cliente | peso | colesterol | id_estado | |
|---|---|---|---|---|
| count | 547.00000 | 542.000000 | 547.000000 | 547.000000 |
| mean | 274.00000 | 143.440959 | 170.433272 | 13.833638 |
| std | 158.04957 | 30.836868 | 39.147189 | 7.593117 |
| min | 1.00000 | 95.000000 | 102.000000 | 1.000000 |
| 25% | 137.50000 | 116.000000 | 136.000000 | 7.000000 |
| 50% | 274.00000 | 140.000000 | 169.000000 | 13.000000 |
| 75% | 410.50000 | 171.000000 | 208.000000 | 20.000000 |
| max | 547.00000 | 203.000000 | 235.000000 | 27.000000 |
estados.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 27 entries, 0 to 26 Data columns (total 4 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 id_estado 27 non-null int64 1 estado 27 non-null object 2 sigla_estado 27 non-null object 3 pais 25 non-null object dtypes: int64(1), object(3) memory usage: 992.0+ bytes
idade_clientes.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 547 entries, 0 to 546 Data columns (total 2 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 id_cliente 547 non-null int64 1 idade 547 non-null int64 dtypes: int64(2) memory usage: 8.7 KB
idade_clientes.describe()
| id_cliente | idade | |
|---|---|---|
| count | 547.00000 | 547.000000 |
| mean | 274.00000 | 42.791590 |
| std | 158.04957 | 15.160209 |
| min | 1.00000 | 16.000000 |
| 25% | 137.50000 | 30.000000 |
| 50% | 274.00000 | 42.000000 |
| 75% | 410.50000 | 55.000000 |
| max | 547.00000 | 70.000000 |
dados_clientes.isna().sum()
id_cliente 0 peso 5 colesterol 0 genero 0 id_estado 0 dtype: int64
estados.isna().sum()
id_estado 0 estado 0 sigla_estado 0 pais 2 dtype: int64
idade_clientes.isna().sum() #não há dados nulos
id_cliente 0 idade 0 dtype: int64
# Substituindo os valores nulos em dados_clientes pela média de pesos:
media = dados_clientes.peso.mean()
dados_clientes.peso.fillna(media, inplace=True)
dados_clientes.isna().sum()
id_cliente 0 peso 0 colesterol 0 genero 0 id_estado 0 dtype: int64
# Preenchendo os valores nulos da data frame estados:
print(estados.pais.value_counts())
Brasil 25 Name: pais, dtype: int64
estados.pais.fillna('Brasil', inplace=True)
estados.isna().sum()
id_estado 0 estado 0 sigla_estado 0 pais 0 dtype: int64
import matplotlib.pyplot as plt
dados_clientes.hist(column='peso', figsize=(15,7), bins=30)
plt.show()
dados_clientes.hist(column='colesterol', figsize=(15,7), bins=100)
plt.show()
idade_clientes.hist(column='idade', figsize=(15,7), bins=100)
plt.show()
from sklearn.preprocessing import LabelEncoder
LE = LabelEncoder()
dados_clientes['genero_number'] = LE.fit_transform(dados_clientes['genero'])
dados_clientes.hist(column='genero_number', figsize=(15,7), bins=100)
plt.show()
base_dados = pd.merge(dados_clientes, idade_clientes, on = 'id_cliente')
base_dados.head()
| id_cliente | peso | colesterol | genero | id_estado | genero_number | idade | |
|---|---|---|---|---|---|---|---|
| 0 | 1 | 102.0 | 111 | Masculino | 23 | 1 | 17 |
| 1 | 2 | 115.0 | 135 | Masculino | 7 | 1 | 28 |
| 2 | 3 | 115.0 | 136 | Masculino | 4 | 1 | 62 |
| 3 | 4 | 140.0 | 167 | Feminino | 24 | 0 | 55 |
| 4 | 5 | 130.0 | 158 | Masculino | 26 | 1 | 44 |
base_dados = pd.merge(base_dados, estados, on = 'id_estado')
base_dados.head()
| id_cliente | peso | colesterol | genero | id_estado | genero_number | idade | estado | sigla_estado | pais | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 102.000000 | 111 | Masculino | 23 | 1 | 17 | Roraima | rr | Brasil |
| 1 | 54 | 149.000000 | 183 | Masculino | 23 | 1 | 48 | Roraima | rr | Brasil |
| 2 | 61 | 113.000000 | 129 | Feminino | 23 | 0 | 70 | Roraima | rr | Brasil |
| 3 | 147 | 143.440959 | 171 | Masculino | 23 | 1 | 40 | Roraima | rr | Brasil |
| 4 | 154 | 134.000000 | 162 | Feminino | 23 | 0 | 26 | Roraima | rr | Brasil |
base_dados.sort_values('id_cliente').reset_index(drop=True)
| id_cliente | peso | colesterol | genero | id_estado | genero_number | idade | estado | sigla_estado | pais | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 102.0 | 111 | Masculino | 23 | 1 | 17 | Roraima | rr | Brasil |
| 1 | 2 | 115.0 | 135 | Masculino | 7 | 1 | 28 | Distrito Federal | df | Brasil |
| 2 | 3 | 115.0 | 136 | Masculino | 4 | 1 | 62 | Amazonas | am | Brasil |
| 3 | 4 | 140.0 | 167 | Feminino | 24 | 0 | 55 | Santa Catarina | sc | Brasil |
| 4 | 5 | 130.0 | 158 | Masculino | 26 | 1 | 44 | Sergipe | se | Brasil |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 542 | 543 | 172.0 | 207 | Masculino | 22 | 1 | 20 | Rondônia | ro | Brasil |
| 543 | 544 | 129.0 | 157 | Masculino | 1 | 1 | 66 | Acre | ac | Brasil |
| 544 | 545 | 107.0 | 115 | Masculino | 6 | 1 | 36 | Ceará | ce | Brasil |
| 545 | 546 | 117.0 | 147 | Masculino | 20 | 1 | 57 | Rio Grande do Norte | rn | Brasil |
| 546 | 547 | 148.0 | 176 | Masculino | 8 | 1 | 61 | Espírito Santo | es | Brasil |
547 rows × 10 columns
base_dados['sigla_estado'] = base_dados['sigla_estado'].apply(lambda x:x.upper())
base_dados.tail()
| id_cliente | peso | colesterol | genero | id_estado | genero_number | idade | estado | sigla_estado | pais | |
|---|---|---|---|---|---|---|---|---|---|---|
| 542 | 452 | 117.0 | 138 | Feminino | 19 | 0 | 61 | Rio de Janeiro | RJ | Brasil |
| 543 | 454 | 106.0 | 115 | Masculino | 19 | 1 | 70 | Rio de Janeiro | RJ | Brasil |
| 544 | 510 | 110.0 | 120 | Feminino | 19 | 0 | 48 | Rio de Janeiro | RJ | Brasil |
| 545 | 516 | 166.0 | 200 | Feminino | 19 | 0 | 47 | Rio de Janeiro | RJ | Brasil |
| 546 | 523 | 103.0 | 110 | Masculino | 19 | 1 | 26 | Rio de Janeiro | RJ | Brasil |
base_dados = base_dados[['id_cliente', 'genero', 'idade', 'peso', 'colesterol' ,'id_estado', 'estado', 'sigla_estado', 'pais' ]].reset_index(drop=True)
base_dados.head()
| id_cliente | genero | idade | peso | colesterol | id_estado | estado | sigla_estado | pais | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | Masculino | 17 | 102.000000 | 111 | 23 | Roraima | RR | Brasil |
| 1 | 54 | Masculino | 48 | 149.000000 | 183 | 23 | Roraima | RR | Brasil |
| 2 | 61 | Feminino | 70 | 113.000000 | 129 | 23 | Roraima | RR | Brasil |
| 3 | 147 | Masculino | 40 | 143.440959 | 171 | 23 | Roraima | RR | Brasil |
| 4 | 154 | Feminino | 26 | 134.000000 | 162 | 23 | Roraima | RR | Brasil |
# Vamos utilizar o algoritmo KMeans para fazer a classificação dos grupos de risco
# Para determinar o número de clusters, iremos utilizar o método do cotovelo
from sklearn.cluster import KMeans
def calcular_wcss(dados):
wcss = []
for k in range(1,11):
kmeans = KMeans(n_clusters = k, random_state=0)
kmeans.fit(X=dados)
wcss.append(kmeans.inertia_)
return wcss
dados = base_dados[['peso','colesterol']]
dados.head()
| peso | colesterol | |
|---|---|---|
| 0 | 102.000000 | 111 |
| 1 | 149.000000 | 183 |
| 2 | 113.000000 | 129 |
| 3 | 143.440959 | 171 |
| 4 | 134.000000 | 162 |
wcss_clientes = calcular_wcss(dados)
C:\Users\lepae\anaconda3\lib\site-packages\sklearn\cluster\_kmeans.py:881: UserWarning: KMeans is known to have a memory leak on Windows with MKL, when there are less chunks than available threads. You can avoid it by setting the environment variable OMP_NUM_THREADS=3. warnings.warn(
# Visualizando os dados obtidos do WCSS
for i in range(len(wcss_clientes)):
print(f'O cluster {i+1} possui valor de WCSS de: {wcss_clientes[i]}')
O cluster 1 possui valor de WCSS de: 1351189.925143519 O cluster 2 possui valor de WCSS de: 332506.8745754446 O cluster 3 possui valor de WCSS de: 142760.53507773773 O cluster 4 possui valor de WCSS de: 83532.28447604488 O cluster 5 possui valor de WCSS de: 55985.607261259174 O cluster 6 possui valor de WCSS de: 39764.47488459907 O cluster 7 possui valor de WCSS de: 29788.64447127613 O cluster 8 possui valor de WCSS de: 24104.658531617915 O cluster 9 possui valor de WCSS de: 19219.619624628987 O cluster 10 possui valor de WCSS de: 16779.8175307252
# Visualizando o gráfico do cotovelo:
import plotly.express as px #Criação de graficos dinâmnicos
import plotly.offline as py
import plotly.graph_objects as go #Para criação e concatenização de graficos
grafico_wcss = px.line( x= range(1,11),
y=wcss_clientes
)
fig = go.Figure(grafico_wcss)
fig.update_layout(title='Calculando o WCSS',
xaxis_title= 'Número de clusters',
yaxis_title= 'Valor do Wcss',
template = 'plotly_white'
)
fig.show()
# Aplicando o KMeans para num. de clisters igual a 4
kmeans_clientes = KMeans(n_clusters=4, random_state=0)
base_dados['cluster'] = kmeans_clientes.fit_predict(dados)
base_dados.head()
| id_cliente | genero | idade | peso | colesterol | id_estado | estado | sigla_estado | pais | cluster | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | Masculino | 17 | 102.000000 | 111 | 23 | Roraima | RR | Brasil | 1 |
| 1 | 54 | Masculino | 48 | 149.000000 | 183 | 23 | Roraima | RR | Brasil | 0 |
| 2 | 61 | Feminino | 70 | 113.000000 | 129 | 23 | Roraima | RR | Brasil | 1 |
| 3 | 147 | Masculino | 40 | 143.440959 | 171 | 23 | Roraima | RR | Brasil | 3 |
| 4 | 154 | Feminino | 26 | 134.000000 | 162 | 23 | Roraima | RR | Brasil | 3 |
# Calculando os centroides de cada cluster:
centroides_clusters = kmeans_clientes.cluster_centers_
centroides_clusters
array([[154.90113776, 190.58333333],
[107.80519481, 121.31818182],
[185.125 , 219.72222222],
[131.01334694, 158.30496454]])
# Plotando o gráfico:
grafico = px.scatter(x = base_dados['peso'],
y = base_dados['colesterol'],
color= base_dados['cluster'])
grafico_centroide = px.scatter(x = centroides_clusters[:,0], y = centroides_clusters[:,1], size = [7,7,7,7])
grafico_final = go.Figure(data = grafico.data + grafico_centroide.data)
grafico_final.update_layout(title='Análise de Clusters',
xaxis_title= 'Colesterol',
yaxis_title= 'Peso',
)
grafico_final.show()
base_dados.loc[base_dados['cluster']==2,'nome_cluster'] = 'Alto Risco'
base_dados.loc[base_dados['cluster']==0,'nome_cluster'] = 'Risco Moderado Alto'
base_dados.loc[base_dados['cluster']==3,'nome_cluster'] = 'Risco Moderado Baixo'
base_dados.loc[base_dados['cluster']==1,'nome_cluster'] = 'Baixo Risco'
base_dados.head()
| id_cliente | genero | idade | peso | colesterol | id_estado | estado | sigla_estado | pais | cluster | nome_cluster | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | Masculino | 17 | 102.000000 | 111 | 23 | Roraima | RR | Brasil | 1 | Baixo Risco |
| 1 | 54 | Masculino | 48 | 149.000000 | 183 | 23 | Roraima | RR | Brasil | 0 | Risco Moderado Alto |
| 2 | 61 | Feminino | 70 | 113.000000 | 129 | 23 | Roraima | RR | Brasil | 1 | Baixo Risco |
| 3 | 147 | Masculino | 40 | 143.440959 | 171 | 23 | Roraima | RR | Brasil | 3 | Risco Moderado Baixo |
| 4 | 154 | Feminino | 26 | 134.000000 | 162 | 23 | Roraima | RR | Brasil | 3 | Risco Moderado Baixo |
# Verifica os valores de idade por cluster
base_dados.groupby('nome_cluster')['idade'].describe()
| count | mean | std | min | 25% | 50% | 75% | max | |
|---|---|---|---|---|---|---|---|---|
| nome_cluster | ||||||||
| Alto Risco | 144.0 | 42.951389 | 15.263801 | 16.0 | 31.75 | 42.5 | 56.0 | 69.0 |
| Baixo Risco | 154.0 | 43.383117 | 15.399770 | 17.0 | 30.25 | 42.0 | 58.0 | 70.0 |
| Risco Moderado Alto | 109.0 | 42.082569 | 15.696855 | 16.0 | 28.00 | 44.0 | 55.0 | 70.0 |
| Risco Moderado Baixo | 140.0 | 42.528571 | 14.482658 | 16.0 | 30.75 | 42.0 | 54.0 | 70.0 |
# Verifica os valores de estado por cluster
base_dados.groupby('nome_cluster')['estado'].describe()
| count | unique | top | freq | |
|---|---|---|---|---|
| nome_cluster | ||||
| Alto Risco | 144 | 27 | Rio Grande do Sul | 9 |
| Baixo Risco | 154 | 27 | Amazonas | 11 |
| Risco Moderado Alto | 109 | 27 | Minas Gerais | 8 |
| Risco Moderado Baixo | 140 | 27 | Piauí | 10 |